package main

import (
	"bytes"
	"encoding/binary"
	"sync"
	"sync/atomic"
	"time"

	"github.com/golang/protobuf/proto"
)

var HouseManager = &RoomManager{
	roomMap: make(map[string]*Room),
	mutex:   new(sync.Mutex),
}

type RoomManager struct {
	roomMap map[string]*Room
	mutex   *sync.Mutex
}

func (roomManager *RoomManager) RemoveRoom(room *Room) {
	logger.Printf("[ROOM - %s] 移除空房间 %v", room.roomNo, *room)
	delete(roomManager.roomMap, room.roomNo)
}

type Room struct {
	roomNo      string
	max         uint8 // 房间允许最大玩家数量
	players     []*Client
	playerCount uint32 // 房间内玩家数量
	frameNum    uint32
	mutex       *sync.Mutex

	started bool
	end     chan int
}

func (room *Room) AddPlayer(player *Client) {

	//room.mutex.Lock()

	room.players[player.PlayerNo] = player
	atomic.AddUint32(&room.playerCount, 1)

	logger.Printf("[ROOM - %s] 玩家 [%s - %d] 加入房间, 房间内玩家数 [%d]", room.roomNo, player.UserId, player.PlayerNo, room.playerCount)

	//room.mutex.Unlock()

}

func (room *Room) RemovePlayer(player *Client) {

	//room.mutex.Lock()

	room.players[player.PlayerNo] = nil
	atomic.AddUint32(&room.playerCount, ^uint32(0))

	logger.Printf("[ROOM - %s] 玩家 [%s - %d] 离开房间, 房间内玩家数 [%d]", room.roomNo, player.UserId, player.PlayerNo, room.playerCount)

	//room.mutex.Unlock()
}

func (room *Room) isEmpty() bool {
	return room.playerCount == 0
}

func (room *Room) isFull() bool {
	return room.playerCount == uint32(room.max)
}

// 结束游戏
func (room *Room) endGame() bool {
	if room.started {
		room.end <- 1
		return true
	}
	return false
}

func (room *Room) TryEndGame() bool {
	if room.isEmpty() {
		return room.endGame()
	}
	return false
}

func (room *Room) startGame() {

	room.started = true

	room.end = make(chan int)

	ticker := time.NewTicker(time.Millisecond * 120)
	defer ticker.Stop()
	defer close(room.end)

	// 分发初始化位置
	room.dispatch()
FOR:
	for {
		select {
		case <-ticker.C:
			room.dispatch()
		case <-room.end:
			logger.Printf("[ROOM - %s] 游戏结束, 关闭帧同步", room.roomNo)
			room.started = false
			break FOR
		}
	}
}

func (room *Room) dispatch() {
	if room.started {
		// 帧号增1
		room.frameNum++

		// 组装房间内所有玩家动作数据
		playersActions := room.assemble()

		// 生成此次推送完整数据包
		buffer := room.datagram(playersActions)

		go room.doDispatch(buffer)

	}
}

func (room *Room) doDispatch(datagram *bytes.Buffer) int8 {

	if datagram == nil {
		return -1
	}

	defer func() {
		if err := recover(); err != nil {
			logger.Printf("[ERR] dispatch: %s", err)
		}
	}()

	var count int8 = 0

	fullData := datagram.Bytes()

	for _, player := range room.players {
		if player != nil {
			player.Send <- fullData
			count++
		} else {
			logger.Printf("[ROOM - %s] 房间内帧同步, 遍历房间内players 出现 nil", room.roomNo)
		}
	}
	return count
}

// 生成完整数据包
func (room *Room) datagram(playersActions *PlayersActions) *bytes.Buffer {
	body, err := proto.Marshal(playersActions)
	if err != nil {
		logger.Println("[ERR] Serialize PlayersActions: ", err)
		return nil
	} else {
		// 业务数据的长度
		bodyLength := len(body)
		// 业务数据 + 指令 的长度
		firstSegLength := bodyLength + 2
		// 整个数据包长度
		datagramLength := firstSegLength + 4

		length := make([]byte, 4)
		binary.BigEndian.PutUint32(length, uint32(firstSegLength))

		// 准备数据包大小缓冲区
		buffer := bytes.NewBuffer(make([]byte, 0, datagramLength))
		// 缓冲区写 指令 + 业务数据 长度
		_, err = buffer.Write(length)

		// 缓冲区写module
		err = binary.Write(buffer, binary.BigEndian, frameSyncPush[0])
		//buffer.Write(moduleBuf.Bytes())

		// 缓冲区写command
		err = binary.Write(buffer, binary.BigEndian, frameSyncPush[1])

		// 缓冲区写业务数据
		_, err = buffer.Write(body)

		if err != nil {
			logger.Println("写数据包错误", err)
			return nil
		}

		return buffer
	}
}

// 组装房间内所有人周期内的动作指令数据
func (room *Room) assemble() *PlayersActions {
	playerSize := len(room.players)

	playersActions := &PlayersActions{
		FrameSeq: room.frameNum,
	}

	fullFrame := make([]*PlayerActions, playerSize)
	for idx := 0; idx < playerSize; idx++ {
		fullFrame[idx] = room.getPlayerActions(idx)
	}

	playersActions.PlayerActions = fullFrame

	return playersActions
}

var emptyByte = []byte("空包")

func (room *Room) getPlayerActions(playerIndex int) *PlayerActions {

	playerActions := &PlayerActions{
		PlayerNo: uint32(room.players[playerIndex].PlayerNo),
	}

	actions := make([][]byte, 10)

	for idx := 0; idx < 10; idx++ {
		selected := room.readWithSelect(playerIndex)
		if selected == nil {
			// 赋予玩家最后一次动作数据
			selected = room.players[playerIndex].LastAction // emptyByte
		}
		actions[idx] = selected
	}
	playerActions.Actions = actions

	return playerActions
}

func (room *Room) readWithSelect(index int) []byte {

	player := room.players[index]
	if player == nil {
		return nil
	}

	select {
	case indexPlayerAction := <-player.Actions:
		return indexPlayerAction
	default:
		return nil
	}
}
